//
// async_tcp_client.cpp
// ~~~~~~~~~~~~~~~~~~~~
//

#include <boost/enable_shared_from_this.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/write.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <string>

using boost::asio::deadline_timer;
using boost::asio::ip::tcp;

class client: public boost::noncopyable,
  public boost::enable_shared_from_this<client>
{
public:
    client(boost::asio::io_service& ios_, std::string address, short port) : stopped_(false), 
      socket_(ios_), resolver_(ios_),deadline_(ios_), address_(address), port_(port)
  {

  }

  void start()
  {
    boost::asio::ip::tcp::resolver::iterator 
            endpoint_iter(boost::asio::ip::tcp::resolver::query(address, port));
    // Start the connect actor.
    start_connect(endpoint_iter);

    // Start the deadline actor
    deadline_.async_wait(boost::bind(&client::check_deadline, this));
  }

  // This function terminates all the actors to shut down the connection. 
  void stop()
  {
    stopped_ = true;
    boost::system::error_code ignored_ec;
    socket_.close(ignored_ec);
    deadline_.cancel();
    //heartbeat_timer_.cancel();
  }

private:
  void start_connect(tcp::resolver::iterator endpoint_iter)
  {
    if (endpoint_iter != tcp::resolver::iterator())
    {
      std::cout << "Trying " << endpoint_iter->endpoint() << "...\n";

      // Set a deadline for the connect operation. 
      // 60秒连接超时
      deadline_.expires_from_now(boost::posix_time::seconds(60));

      // Start the asynchronous connect operation.
      socket_.async_connect(endpoint_iter->endpoint(), 
            boost::bind(&client::handle_connect, this, _1, endpoint_iter));
    }
    else
    {
      // Shut down the client.
      stop();
    }
  }

  void handle_connect(const boost::system::error_code& ec,
            tcp::resolver::iterator endpoint_iter)
  {
    if (stopped_)
      return;

    //If the socket is closed at this time then the timeout handler must have run first.
    if (!socket_.is_open())
    {
      std::cout << "Connect timed out\n";

      // Try the next available endpoint.
      start_connect(++endpoint_iter);
    }
    else if (ec)
    {
      std::cout << "Connect error: " << ec.message() << "\n";
      // close the socket 
      socket_.close();
      //stop();

      // Try the next available endpoint.
      start_connect(++endpoint_iter);
    }
    else // successfully connection.
    {
      std::cout << "Connected to " << endpoint_iter->endpoint() << "\n";

      // Callback on Successfully connection
      boost::shared_ptr<client> pClient = this->shared_from_this();
      if (cb_connect)
          cb_connect(pClient);

      // Start the read actor.
      start_read();
    }
  }

  void start_read()
  {
    // Set a deadline for the read operation.
    deadline_.expires_from_now(boost::posix_time::seconds(30));

    // Start an asyn read
    boost::asio::async_read_until

    boost::asio::async_read_some(socket_, read_buffer_,
        boost::bind(&client::handle_read, this, _1, _2));
    //boost::asio::async_read_until(socket_, input_buffer_, '\n',
      //  boost::bind(&client::handle_read, this, _1));
  }

  void handle_read(const boost::system::error_code& ec, int bytes_)
  {
    if (stopped_)
      return;

    if (!ec)
    {
      // an Serializer message from the buffer.
      boost::shared_ptr<client> pClient = this->shared_from_this();
      if (cb_read)
          cb_read(pClient, read_buffer_)
      //继续读  
      start_read();
    }
    else
    {
      std::cout << "Error on receive: " << ec.message() << "\n";

      stop();
    }
  }

  void start_write()
  {
    if (stopped_)
      return;

    //start asyn send
    boost::asio::async_write(socket_, boost::asio::buffer("\n", 1),
        boost::bind(&client::handle_write, this, _1));
  }

  void handle_write(const boost::system::error_code& ec)
  {
    if (stopped_)
      return;

    if (ec)
    {
      std::cout << "Error on heartbeat: " << ec.message() << "\n";

      stop();
    }
  }

  void check_deadline()
  {
    if (stopped_)
      return;

    // Check whether the deadline has passed.
    if (deadline_.expires_at() <= deadline_timer::traits_type::now())
    {
      // The deadline has passed.
      //socket_.close();
      std::cout << "timer out on check_deadline \n";

      stop();
      return ;
    }

    // Put the actor back to sleep.
    deadline_.async_wait(boost::bind(&client::check_deadline, this));
  }

private:
  bool stopped_;
  boost::asio::ip::tcp::resolver resolver_;
  boost::asio::ip::tcp::socket socket_;

  //读写缓冲
  boost::asio::streambuf read_buffer_;
  boost::asio::streambuf write_buffer_;
  //连接地址
  std::string address_;
  short port_;
  //超时timer
  boost::asio::deadline_timer deadline_;

public:
  //conect_succ_call_back   cb_connect;
  //read_some_call_back     cb_read;
  //close_call_back         cb_close;
};

/*

client c(); //init

//连接成功回调
boost::asio::spawn(ios, boost::bind(&client::start, c, 
    boost::bind(&client_manager::connect_succ_call_back, c, mgr)), _1);

//读回调
boost::asio::spawn(iso, boost::bind(&client::read, c, 
    boost::bind(&client_manager::read_succ_call_back, c, mgr, _1)), _1);
*/
//写回调
